/*
 * engineAB.c
 *
 *  Created on: 07/05/2012
 *      Author: GrupoAB
 */

#include <stdlib.h>
#include <string.h>
#include <signal.h>

#include <stdbool.h>
#include <memcached/config_parser.h>

#include "engineAB.h"


/*
 * Estas son las funciones estaticas necesarias para que el engine funcione
 */

static ENGINE_ERROR_CODE engineAB_initialize(ENGINE_HANDLE* , const char* config_str);
static void engineAB_destroy(ENGINE_HANDLE*, const bool force);

static ENGINE_ERROR_CODE engineAB_allocate(ENGINE_HANDLE* , const void* cookie, item **item, const void* key,
											const size_t nkey, const size_t nbytes, const int flags, const rel_time_t exptime);
static bool engineAB_get_item_info(ENGINE_HANDLE *, const void *cookie, const item* item, item_info *item_info);
static ENGINE_ERROR_CODE engineAB_store(ENGINE_HANDLE* , const void *cookie, item* item, uint64_t *cas,
											ENGINE_STORE_OPERATION operation, uint16_t vbucket);
static void engineAB_item_release(ENGINE_HANDLE* , const void *cookie, item* item);
static ENGINE_ERROR_CODE engineAB_get(ENGINE_HANDLE* , const void* cookie, item** item, const void* key, const int nkey, uint16_t vbucket);

static ENGINE_ERROR_CODE engineAB_flush(ENGINE_HANDLE* , const void* cookie, time_t when);

static ENGINE_ERROR_CODE engineAB_item_delete(ENGINE_HANDLE* , const void* cookie, const void* key, const size_t nkey, uint64_t cas, uint16_t vbucket);

/*
 * ************************** Dummy Functions **************************
 *
 * Estas funciones son dummy, son necesarias para que el engine las tengas
 * pero no tienen logica alguna y no seran necesarias implementar
 *
 */

static const engine_info* engineAB_get_info(ENGINE_HANDLE* );
static ENGINE_ERROR_CODE engineAB_get_stats(ENGINE_HANDLE* , const void* cookie, const char* stat_key, int nkey, ADD_STAT add_stat);
static void engineAB_reset_stats(ENGINE_HANDLE* , const void *cookie);
static ENGINE_ERROR_CODE engineAB_unknown_command(ENGINE_HANDLE* , const void* cookie, protocol_binary_request_header *request, ADD_RESPONSE response);
static void engineAB_item_set_cas(ENGINE_HANDLE *, const void *cookie, item* item, uint64_t val);

/**********************************************************************/

//Se comentan porque tienen que ver con la estructura de la cache, no con el motor
// void engineAB_dummp(int signal);

// t_dictionary *cache;


MEMCACHED_PUBLIC_API ENGINE_ERROR_CODE create_instance(uint64_t interface, GET_SERVER_API get_server_api, ENGINE_HANDLE **handle) {

	if (interface == 0) {
		return ENGINE_ENOTSUP;
	}

	t_engineAB *engine = calloc(1, sizeof(t_engineAB));
	if (engine == NULL) {
		return ENGINE_ENOMEM;
	}


	engine->engine.interface.interface = 1;

	engine->engine.initialize = engineAB_initialize;
	engine->engine.destroy = engineAB_destroy;
	engine->engine.get_info = engineAB_get_info;
	engine->engine.allocate = engineAB_allocate;
	engine->engine.remove = engineAB_item_delete;
	engine->engine.release = engineAB_item_release;
	engine->engine.get = engineAB_get;
	engine->engine.get_stats = engineAB_get_stats;
	engine->engine.reset_stats = engineAB_reset_stats;
	engine->engine.store = engineAB_store;
	engine->engine.flush = engineAB_flush;
	engine->engine.unknown_command = engineAB_unknown_command;
	engine->engine.item_set_cas = engineAB_item_set_cas;
	engine->engine.get_item_info = engineAB_get_item_info;

	engine->get_server_api = get_server_api;

	*handle = (ENGINE_HANDLE*) engine;

	//Comentado porque tiene que ver conla estructura de la cache, no el motor
	//void _cache_item_destroy(void *item){


	//	((t_engineAB_item*)item)->stored = false;

	//	engineAB_item_release(NULL, NULL, item);
	//}

	//cache = dictionary_create(_cache_item_destroy);

	return ENGINE_SUCCESS;
}


static ENGINE_ERROR_CODE engineAB_initialize(ENGINE_HANDLE* handle, const char* config_str){

	t_engineAB *engine = (t_engineAB*)handle;



	if (config_str != NULL) {
	  struct config_item items[] = {
		 { .key = "cache_size",
		   .datatype = DT_SIZE,
		   .value.dt_size = &engine->config.cache_max_size },
		 { .key = "chunk_size",
		   .datatype = DT_SIZE,
		   .value.dt_size = &engine->config.chunk_size },
		 { .key = "item_size_max",
		   .datatype = DT_SIZE,
		   .value.dt_size = &engine->config.block_size_max },
		 { .key = NULL}
	  };

		parse_config(config_str, items, NULL);
	}

	//Comentado porque es para hacer dump al contenido de la cache, la cual no tiene estructura actualmente
	// signal(SIGUSR1, engineAB_dummp);

	return ENGINE_SUCCESS;
}



static void engineAB_destroy(ENGINE_HANDLE* handle, const bool force){
	free(handle);
}


static const engine_info* engineAB_get_info(ENGINE_HANDLE* handle) {
	static engine_info info = {
	          .description = "GrupoAB engine 1.0",
	          .num_features = 0,
	          .features = {
	               [0].feature = ENGINE_FEATURE_LRU,
	               [0].description = "No hay soporte de LRU-FIFO-Particiones"
	           }
	};

	return &info;
}

static ENGINE_ERROR_CODE engineAB_allocate(ENGINE_HANDLE *handler, const void* cookie, item **item, const void* key,
											const size_t nkey, const size_t nbytes, const int flags, const rel_time_t exptime){

	t_engineAB_item *it = malloc( sizeof(t_engineAB_item) );

		if (it == NULL) {
			return ENGINE_ENOMEM;
		}

		it->flags = flags;
		it->exptime = exptime;
		it->nkey = nkey;
		it->ndata = nbytes;
		it->key = malloc(nkey);
		it->data = malloc(nbytes);
		it->stored = false;

		memcpy(it->key, key, nkey);
		*item = it;
	engineAB_item_release(handler,NULL,it);
		return ENGINE_SUCCESS;
}


/*
 * Destruye un item allocado, esta es una función que tiene que utilizar internamente mecached
 */
static void engineAB_item_release(ENGINE_HANDLE *handler, const void *cookie, item* item){
	t_engineAB_item *it = (t_engineAB_item*)item;
	if( !it->stored ){
		free(it->key);
		free(it->data);
		free(it);
	}
}

/*
 * Esta función lo que hace es mapear el item_info el cual es el tipo que memcached sabe manejar con el tipo de item
 * nuestro el cual es el que nosotros manejamos
 */
static bool engineAB_get_item_info(ENGINE_HANDLE *handler, const void *cookie, const item* item, item_info *item_info){
	// casteamos de item*, el cual es la forma generica en la cual memcached trata a nuestro tipo de item, al tipo
	// correspondiente que nosotros utilizamos
	t_engineAB_item *it = (t_engineAB_item*)item;

	if (item_info->nvalue < 1) {
	  return false;
	}

	item_info->cas = 0; 		/* Not supported */
	item_info->clsid = 0; 		/* Not supported */
	item_info->exptime = it->exptime;
	item_info->flags = it->flags;
	item_info->key = it->key;
	item_info->nkey = it->nkey;
	item_info->nbytes = it->ndata; 	/* Total length of the items data */
	item_info->nvalue = 1; 			/* Number of fragments used ( Default ) */
	item_info->value[0].iov_base = it->data; /* Hacemos apuntar item_info al comienzo de la info */
	item_info->value[0].iov_len = it->ndata; /* Le seteamos al item_info el tamaño de la información */

	return true;
}

/*
 * Esta funcion es invocada cuando memcached recibe el comando get
 */
static ENGINE_ERROR_CODE engineAB_get(ENGINE_HANDLE *handle, const void* cookie, item** item, const void* key, const int nkey, uint16_t vbucket){
	// transformamos  la variable key a string
	//char strkey[nkey + 1];
	//memcpy(strkey, key, nkey);
	//strkey[nkey] = '\0';

	t_engineAB_item *it = malloc( sizeof(t_engineAB_item) );
		it->flags = 0;
		it->exptime = 0;
		it->nkey = nkey;
		it->ndata = nkey;
		it->key = malloc(nkey);
		it->data = malloc(it->ndata);
		it->stored = true;

		memcpy(it->key, key, nkey);
		memcpy(it->data, key, nkey);

	// Esto se comenta porque no hay una estructura de cache actualmente
	// buscamos y obtenemos el item
	//t_engineAB_item *it = dictionary_get(cache, strkey);


	if( it == NULL ){
		return ENGINE_NOT_STORED;
	}

	//retornamos el item
	*item = it;

	engineAB_item_release(handle,NULL,it);
	return ENGINE_SUCCESS;
}

/*
 * Esta función se llama cuando memcached recibe un set. La variable operation nos indica el tipo. Estos deben ser tratados indistintamente
 */
static ENGINE_ERROR_CODE engineAB_store(ENGINE_HANDLE *handle, const void *cookie, item* item, uint64_t *cas, ENGINE_STORE_OPERATION operation, uint16_t vbucket){
	t_engineAB_item *it = (t_engineAB_item*)item;

		char *strkey = malloc(it->nkey + 1);
		memcpy(strkey, it->key, it->nkey);
		strkey[it->nkey] = '\0';

		it->stored = true;

		//comentada porque no hay una estructura de cache actualmente
		//dictionary_put(cache, strkey, it);

	   *cas = 0;

   return ENGINE_SUCCESS;
}

/*
 * Esta función se llama cuando memcached recibe un flush_all
 */
static ENGINE_ERROR_CODE engineAB_flush(ENGINE_HANDLE* handle, const void* cookie, time_t when) {

	//Comentada porque no hay una estructura de cache actualmente
	//limpio toda la cache
	//dictionary_clean(cache);

	return ENGINE_SUCCESS;
}

/*
 * Esta función se llama cuando memcached recibe un delete
 */
static ENGINE_ERROR_CODE engineAB_item_delete(ENGINE_HANDLE* handle, const void* cookie, const void* key, const size_t nkey, uint64_t cas, uint16_t vbucket) {
	char strkey[nkey + 1];
	memcpy(strkey, key, nkey);
	strkey[nkey] = '\0';

	//Comentado porque es la eliminacion por parte de la cache
	//void *item = dictionary_remove(cache, strkey);

	//if( item == NULL ) {
	//	return ENGINE_KEY_ENOENT;
	//}

	//Eliminacion por parte del motor

	t_engineAB_item *item = malloc( sizeof(t_engineAB_item) );
	item->stored = false;
	item->flags = 0;
	item->exptime = 0;
	item->nkey = nkey;
	item->ndata = nkey;
	item->key = malloc(nkey);
	item->data = malloc(item->ndata);


	engineAB_item_release(handle, NULL, item);

	return ENGINE_SUCCESS;
}

/*
 * ************************************* Funciones Dummy *************************************
 */

static ENGINE_ERROR_CODE engineAB_get_stats(ENGINE_HANDLE* handle, const void* cookie, const char* stat_key, int nkey, ADD_STAT add_stat) {
	return ENGINE_SUCCESS;
}

static void engineAB_reset_stats(ENGINE_HANDLE* handle, const void *cookie) {

}

static ENGINE_ERROR_CODE engineAB_unknown_command(ENGINE_HANDLE* handle, const void* cookie, protocol_binary_request_header *request, ADD_RESPONSE response) {
	return ENGINE_ENOTSUP;
}

static void engineAB_item_set_cas(ENGINE_HANDLE *handle, const void *cookie, item* item, uint64_t val) {

}


/*
 * Handler de la SIGUSR1
 */
//Comentado porque es la dump de la cache la cual no tiene estructura actualmente
//void engineAB_dummp(int signal){

//	void it(char *key, void *data){
//		printf("%s\n", key);
//	}

//	dictionary_iterator(cache, it);
//}
